package cn.tedu.v2_1;

import java.io.*;
import java.net.BindException;
import java.net.ServerSocket;
import java.net.Socket;

/**
 *  Version  2.1   多人聊天室：
 *  知识点：多线程并发
 *  实现步骤：1.Server类--创建多线程CS：目的：处理多个客户端发来的链接
 *         1.1找到Server类--主方法下：加入内部类多线程CS；
 *         目的：供本类任意地方使用
 *         结构：private class CS implements Runnable{}
 *         1.2在内部类多线程CS--私有化属性：Socket--表示线程中socket对象
 *         在CS多线程类：private  Socket socket;
 *         1.3创建构造器
 *         目的：通过线程创建--传入一个通过IP+端口号链接成功客户端socket
 *         public CS(Socket s){this.socket=s;}
 *         参数：Socket s---start启动方法中：获取通过IP+端口
 *               返回正确客户端socket
 *         理解：已经拨通服务器端的客户端
 *         语法块：this.socket=s;
 *         理解：当前线程中socket=获取通过IP+端口，返回正确客户端：
 *         1.4如果已经通过IP+端口得到socket中的正确读入
 *         理解：来一个客户端---服务器端分配：专门socket，进行读入操作
 *         对应：CS线程中 run()   实现读入功能
 *      2.Server类，start方法：第一件事：无限次接收客户端
 *                           第二件事：利用线程--客户端分配正确连接
 *                                  进行读入操作
 */

public class Server {
    private ServerSocket serverSocket;
    public Server() {
        try {
            serverSocket=new ServerSocket(4545);
        } catch (BindException e){
            System.out.println("端口号重复申请，建议换个端口号----");
    } catch (IOException e) {
         System.out.println("申请端口号失败----");
    }
    }

    public void star() {
      //二服务器端启动start：  第一件事：接收服务器链接
        //                   第二件事：CS线程类：处理所有链接服务器端客户端
                                         //处理个个客户端的写入功能--【读取】

        try {
            while(true) {
                //第一件事：无限次接受客户端链接，返回客户端socket
            System.out.println("————等待客户端连接——————");
           Socket socket= serverSocket.accept();

           String ip= socket.getInetAddress().getHostAddress();
            System.out.println(ip+"客户端连接成功");
            //第二件事：接受一个客户端连接--交给线程处理---读入功能
                //效果：链接上一个客户端--有一个服务器端，正确读入操作
                //多线程并发解决方案：来客户端--服务器使用线程--创建客户端链接
                CS cs=new CS(socket);   //1.通过链接,返回一个socket
                Thread t1=new Thread(cs);//2.交给线程--读入功能
                t1.start();//3.启动线程

           }
        } catch (IOException e) {
            System.out.println("服务器端通讯断开");
        } finally {
        }


    }
    public static void main(String[] args) throws IOException {

        Server server=new Server();
        server.star();
    }
    //1.多线程CS状态：处理多个客户端连接的线程类---读入存入线程中
    //使用内部类原因：多线程在Server中可以随时使用--读入方法
    //问题：一个客户端连接服务器，需要流操作：一个客户端与一个服务器端说话【卡住】
    //把服务器端--放入线程中--目的：来一个客户端--配上服务器写
    private class CS implements Runnable{
        //3.定义私有属性：给当前多线程类使用
        private Socket socket;  //返回一个客户端通讯：socket

        //4.多个客户端--交给CS构造器确定--那个客户端---进行读入操作
        public CS(Socket s){
            //5.通过线程创建--传入socket---确定就是当前线程socket
            this.socket=s;

        }
        //2.服务器端功能：读入---加入线程中
        public void run() {
            BufferedReader br=null;
            try {
            InputStream is= socket.getInputStream();
            InputStreamReader  isr=new InputStreamReader(is,"UTF8");
            br=new BufferedReader(isr);
            String str=null;
            while(true){
                str= br.readLine();
                System.out.println("客户端说的话："+str);
            }
            }catch(NullPointerException e){
                System.out.println("IO流传入对象，" +
                        "有对象为null，立马核对，" +
                        "查看try块种对象可能赋值为null");

            }catch(UnsupportedEncodingException e){
                System.out.println("客户端或服务器端：字符集不一致哦");
            }
            catch (IOException e) {
                System.out.println("通讯过程中，传输出现错误！");
            }catch(Exception e){
                e.printStackTrace();
            } finally {
                if (br!=null){
                    try {
                        br.close();
                    } catch (IOException e) {
                        System.out.println("结束通讯，关闭失败");
                    }
                }
                if (socket!=null){
                    try {
                        socket.close();
                    } catch (IOException e) {
                        System.out.println("结束通讯，关闭失败");
                    }
                }
            }
        }
    }

}
